#pragma once
#include "cStream.h"
#include "macros.h"

#define INVALID_PACKET_ID		255

#define PACKET_DISCONNECT_ID	0
#define PACKET_DISCONNECT		cDisconnectPacket
#define PACKET_LOGIN_ID			1
#define PACKET_LOGIN			cLoginResponsePacket
#define PACKET_MESSAGE_ID		2
#define PACKET_MESSAGE			cMessagePacket
#define PACKET_SELF_INFO_ID		3
#define PACKET_SELF_INFO		cSelfInfo
#define PACKET_KEEPALIVE_ID		7
#define PACKET_KEEPALIVE		cKeepAlivePacket
#define PACKET_USERMNGLIST_ID	8
#define PACKET_USERMNGLIST		cUserMngListPacket
#define PACKET_TERMINATOR_ID	254
#define PACKET_TERMINATOR		cTerminatorPacket
#define PACKET_LAST_ID			15
#define PACKET_CHAT_BROADCAST	

#define CPKT_DISCONNECT_ID		0
// str username; bool hashRequest?; str hash
#define CPKT_LOGIN_ID			1
// byte bitmask
#define CPKT_SELF_INFO_ID		2
// int id, short score
#define CPKT_UPDATE_SCORE_ID	3
// str channel, str message
#define CPKT_CHAT_SEND_ID		4
// byte request id
#define CPKT_REQUESTMESSAGE_ID  5
// int recipient, string title, string body
#define CPKT_MESSAGE_SEND_ID	6
// str partialName
#define CPKT_SEARCH_USERS_ID	7
// int userid, byte group_id [PRIVILEGED]
#define CPKT_SET_USER_GROUP_ID	8
// int userid, bool chat_allowed [PRIVILEGED]
#define CPKT_SET_USER_MUTE_ID	9
// str name, str username, str pass, str teachername, byte group [PRIVILEGED]
#define CPKT_CREATE_USER_ID		10
// int userid [PRIVILEGED]
#define CPKT_REMOVE_USER_ID		11
// nothing
#define CPKT_KEEPALIVE_ID		12
// int userid, str pass
#define CPKT_RESET_PASS_ID		13
// int userid
#define CPKT_ADMIN_STATS_ID		14
// nothing
#define CPKT_TEST_LIST_ID		15
// str testName
#define CPKT_ADD_TEST_ID		16
// int testid
#define CPKT_TEST_QUESTION_LIST_ID		17
// int testid, str question, str correct, str wrong, str wrong, str wrong
#define CPKT_TEST_QUESTION_ADD_ID		18
// int questionid
#define CPKT_TEST_QUESTION_REMOVE_ID	19
// int testid
#define CPKT_TEST_REMOVE_ID		20
// bool only_homeworks
#define CPKT_SELF_TESTS_ID		21
// int userid, int testid, int datedue
#define CPKT_SET_HOMEWORK_ID	22
// nothing
#define CPKT_TEST_LIST_ID_ALT	23
// int testid
#define CPKT_TEST_GET			24


class cPacketBase {
public:
	cPacketBase( int id );
	cPacketBase();
	
	virtual cStream getStream();
	virtual unsigned int length();
	unsigned char GetID() { return id; }
	
	static bool isValidID( int id );
protected:
	unsigned char id;
};

class cRawPacket : public cPacketBase {
public:
	cRawPacket( cStream str ) { stream = str; id = str.getFirstByte(); };
	
	virtual cStream getStream() { return stream; }
	virtual unsigned int length() { return 0; }
	
protected:
	cStream stream;
};


class cMessagePacket : public cPacketBase {
public:
	cMessagePacket(std::string msg);
	virtual cStream getStream();
	virtual unsigned int length();
protected:
	std::string message;
};

class cDisconnectPacket : public cPacketBase {
public:
	cDisconnectPacket(std::string msg);
	virtual cStream getStream();
	virtual unsigned int length();
protected:
	std::string message;
};

class cLoginResponsePacket : public cPacketBase {
public:
	cLoginResponsePacket(std::string salt);
	cLoginResponsePacket(bool success);
	virtual cStream getStream();
	virtual unsigned int length();
protected:
	bool is_message;
	std::string salt;
	unsigned char result;
};

class cSelfInfo : public cPacketBase {
public:
	cSelfInfo() : cPacketBase(PACKET_SELF_INFO_ID) 
		{ username = NULL; teachername = NULL; last_login = -1; chat_access = -1; user_group = -1; user_id = -1; }
	~cSelfInfo() { if(username) delete username; if(teachername) delete teachername; }
	virtual cStream getStream() {
		cStream s = cPacketBase::getStream();
		s.writeByte( (username?1:0) + (teachername?2:0) + (last_login!=-1?4:0) + (chat_access!=-1?8:0) + (user_group!=-1?16:0) + (user_id!=-1?32:0) );
		if(username) s.writeString(*username);
		if(teachername) s.writeString(*teachername);
		if(last_login != -1) s.writeInt(last_login);
		if(chat_access != -1) s.writeByte((char)chat_access);
		if(user_group != -1) s.writeByte((char)user_group);
		if(user_id != -1) s.writeInt(user_id);
		return s;
	}
	virtual unsigned int length() {return 0;}
	std::string* username;
	std::string* teachername;
	long last_login;
	int user_id;
	int chat_access;
	int user_group;
};

struct UserMngListUser_st {
	unsigned int userid;
	std::string fullname;
	std::string username;
	std::string teacher;
	int groupid;
};

class cUserMngListPacket : public cPacketBase {
public:
	cUserMngListPacket();
	void addUser(unsigned int userid, std::string fullname, std::string username, std::string teacher, int groupid);
	virtual cStream getStream();
	virtual unsigned int length();
protected:
	std::vector<UserMngListUser_st> users;
};


class cKeepAlivePacket : public cPacketBase {
public:
	cKeepAlivePacket() : cPacketBase(PACKET_KEEPALIVE_ID) {};
	virtual cStream getStream() { return cPacketBase::getStream(); }
	virtual unsigned int length() { return cPacketBase::length(); }
};

class cTerminatorPacket : public cPacketBase { // used to break connection, nothing is actually sent
public:
	cTerminatorPacket();
	virtual cStream getStream() { return cStream(); }
	virtual unsigned int length() { return 0; }
};

